#include "TargetCube.h"
#include "IndexBuffer.h"
#include "Renderer.h"
#include "ShaderProgram.h"
#include "VertexArray.h"
#include "VertexBuffer.h"
#include "VertexBufferLayout.h"
#include <glm/ext/matrix_transform.hpp>
#include <glad/glad.h>

TargetCube::TargetCube() : m_position(glm::vec3(0.f, 0.f, 0.f)), m_scale(glm::vec3(1.f, 1.f, 1.f)), m_angle(glm::vec3(0.f, 0.f, 0.f))
{
    float vertices[8][3] = {
        {-0.5f, -0.5f, -0.5f},
        {-0.5f, 0.5f,  -0.5f},
        {0.5f,  0.5f,  -0.5f},
        {0.5f,  -0.5f, -0.5f},
        {-0.5f, -0.5f, 0.5f },
        {-0.5f, 0.5f,  0.5f },
        {0.5f,  0.5f,  0.5f },
        {0.5f,  -0.5f, 0.5f }
    };

    unsigned int indices[6][6] = {
        {0, 1, 2, 2, 3, 0},
        {4, 5, 6, 6, 7, 4},
        {0, 1, 4, 1, 5, 4},
        {2, 3, 7, 2, 6, 7},
        {0, 3, 4, 3, 7, 4},
        {1, 2, 6, 1, 5, 6}
    };

    for (int i = 0; i < 8; i++) {
        CubeVertexElement elem;
        elem.position = glm::vec3(vertices[i][0], vertices[i][1], vertices[i][2]);
        elem.color    = glm::vec3(i / 8.f, i / 8.f, i / 8.f);
        elem.texture  = glm::vec2(0.f, 0.f);
        m_vertices.push_back(elem);
    }

    for (int i = 0; i < 6; i++) {
        for (int j = 0; j < 6; j++) {
            m_indices.push_back(indices[i][j]);
        }
    }
    updateModel();

    m_vao = std::make_shared<VertexArray>();
    m_vbo = std::make_shared<VertexBuffer>(m_vertices.data(), m_vertices.size() * sizeof(CubeVertexElement));
    m_ibo = std::make_shared<IndexBuffer>(m_indices.data(), m_indices.size());

    VertexBufferLayout layout(sizeof(CubeVertexElement));
    layout.addAttribute(0, 3, GL_FLOAT, offsetof(CubeVertexElement, position));
    layout.addAttribute(1, 3, GL_FLOAT, offsetof(CubeVertexElement, color));
    m_vao->addBuffer(*m_vbo, layout);

    m_shader = std::make_shared<ShaderProgram>("./shaders/example_cube/vertex.shader", "./shaders/example_cube/fragment.shader");
}

TargetCube::~TargetCube()
{
}

void TargetCube::updateModel()
{
    m_model = glm::translate(glm::mat4(1.0f), m_position);
    m_model = glm::rotate(m_model, glm::radians(m_angle.x), glm::vec3(1.f, 0.f, 0.f));
    m_model = glm::rotate(m_model, glm::radians(m_angle.y), glm::vec3(0.f, 1.f, 0.f));
    m_model = glm::rotate(m_model, glm::radians(m_angle.z), glm::vec3(0.f, 0.f, 1.f));
    m_model = glm::scale(m_model, m_scale);
}

void TargetCube::setPosition(glm::vec3 position)
{
    m_position = position;
    updateModel();
}

void TargetCube::setScale(glm::vec3 scale)
{
    m_scale = scale;
    updateModel();
}

void TargetCube::setRotate(glm::vec3 angle)
{
    m_angle = angle;
    updateModel();
}

void TargetCube::render(glm::mat4 view, glm::mat4 proj)
{
    m_shader->bind();
    m_shader->setUniformMat4fv("u_model", m_model);
    m_shader->setUniformMat4fv("u_proj", proj);
    m_shader->setUniformMat4fv("u_view", view);

    GLCall(glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0));
}